/*  
 * i-OSGi - Tunable Bundle Isolation for OSGi
 * Copyright (C) 2011  Sven Schulz
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.iosgi.ie.process;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.SocketException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.ServiceProperty;
import org.iosgi.Constants;
import org.iosgi.IsolationEnvironmentFactory;
import org.iosgi.impl.Debug;
import org.iosgi.impl.EnvironmentIDs;
import org.iosgi.impl.Utils;
import org.iosgi.util.io.Streams;
import org.osgi.framework.BundleContext;

/**
 * @author Sven Schulz
 */
@Component(immediate = true)
@Provides(specifications = { IsolationEnvironmentFactory.class })
public class ProcessIEFactory implements IsolationEnvironmentFactory {

	private static final AtomicLong nextId = new AtomicLong(1);

	@SuppressWarnings("unused")
	@ServiceProperty(name = Constants.R_OSGI_REGISTRATION)
	private boolean publishRemotely = Boolean.TRUE;

	/*
	 * Store process instances to prevent them being garbage collected (kills
	 * process)!
	 */
	private final Set<Process> processes;

	private final URI id;
	private final BundleContext context;

	ProcessIEFactory(final BundleContext context) throws SocketException {
		this.context = context;
		processes = Collections.synchronizedSet(new HashSet<Process>());
		id = EnvironmentIDs.getEnvironmentId(0);
	}

	@Override
	public URI getId() {
		return id;
	}

	@Override
	public URI newIsolationEnvironment(final Map<String, Object> properties)
			throws IOException, InterruptedException, TimeoutException {
		ProcessBuilder builder = new ProcessBuilder();
		String exe = System.getProperty("java.home") + File.separator + "bin"
				+ File.separator;
		String osname = System.getProperty("os.name").toLowerCase();
		if (osname.contains("win")) {
			exe += "javaw.exe";
		} else {
			exe += "java";
		}
		File workDir = new File(".").getAbsoluteFile();
		builder.directory(workDir);
		long id = nextId.getAndIncrement();
		File rootDir = new File(workDir.getCanonicalPath(), "work"
				+ File.separator + id);
		rootDir.mkdirs();
		rootDir.deleteOnExit();
		File configFile = new File(rootDir.getCanonicalPath(),
				"config.properties");
		Properties config = new Properties();
		File autoDeployDir = new File("./bundle").getAbsoluteFile();
		config.setProperty("felix.auto.deploy.dir",
				autoDeployDir.getAbsolutePath());
		config.setProperty("felix.auto.deploy.action", "install,start");
		config.setProperty("org.iosgi.util.configuration.dir",
				"./conf/process/");
		config.store(new FileOutputStream(configFile),
				"This file has been generated by i-OSGi");
		File cacheDir = new File(rootDir.getCanonicalPath(), "felix-cache");
		List<String> command = new ArrayList<String>();
		command.add(exe);
		command.add("-Dfelix.config.properties="
				+ configFile.getAbsoluteFile().toURI());
		if (Debug.isDebugEnabled()) {
			command.add("-Xdebug");
			command.add("-Xrunjdwp:transport=dt_socket,server=y,suspend="
					+ (Debug.isSuspendEnabled() ? "y" : "n") + ",address="
					+ Long.toString(10000 + id));
		}
		URI nid = URI.create(this.getId().toString() + "/" + id);
		command.add("-D" + Constants.ENVIRONMENT_ID + "=" + nid);
		/* FIXME This is a workaround as R-OSGi apparently can't reuse ports. */
		command.add("-Dch.ethz.iks.r_osgi.port="
				+ Integer.toString(9278 + (int) (id % 10000)));
		command.add("-jar");
		command.add("org.apache.felix.main-3.0.6.jar");
		command.add(cacheDir.getAbsolutePath());
		builder.command(command);
		Process p = builder.start();
		Streams.drain(p.getInputStream(), new File(rootDir, "out"));
		Streams.drain(p.getErrorStream(), new File(rootDir, "err"));
		processes.add(p);
		Utils.waitForIsolatedFramework(context, nid, 1, TimeUnit.MINUTES);
		return nid;
	}

}